package com.supermap.filetransfer.execute.download;

import android.util.Log;

import com.supermap.filetransfer.config.ConstantConfigs;
import com.supermap.filetransfer.entity.DownloadEntity;
import com.supermap.filetransfer.enums.ReinitializationPattern;
import com.supermap.filetransfer.enums.WorkState;
import com.supermap.filetransfer.execute.Tasker;
import com.supermap.filetransfer.listeners.IDownloadListener;
import com.supermap.filetransfer.network.listeners.IHttpRequestListener;
import com.supermap.filetransfer.thread.DownloadThread;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.math.BigDecimal;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import okhttp3.Call;
import okhttp3.Response;

/**
 * @author Huang.MinJie
 * @desc 下载任务处理器（主线程类）
 * @date 2022-06-27 14:30
 */
/**
 * TODO 续传点可能有问题请检查
 */

/**
 * 待优化内容
 * 1. 限速下载
 * 2. 连接时长
 */
public class DownloadTasker extends Tasker {
    private final String TAG = "DownloadTasker: ";


    // private boolean init = false;

    /**
     * 下载的文件储存目录
     */
    private String dirFile; // 下载的文件储存目录

    /**
     * 下载文件名称
     */
    private String fileName; // 下载文件名称

    /**
     * 下载链接
     */
    private String url; // 下载链接

    /**
     * 是否支持断点续传
     */
    private File dir;

    /**
     * 下载任务长度总长度
     */
    private long downloadSize = 0; // 下载任务长度总长度

    /**
     * 下载监听器
     */
    private IDownloadListener listener; // 下载监听器

    /**
     * 下载位置
     */
    public volatile long currentLocation = 0; // 下载数量

    /**
     * 下载子任务集合
     */
    private volatile List<DownloadThread> subTasker = new ArrayList(ConstantConfigs.threadNum); //下载子任务

    /**
     * 失败任务
     */
    private volatile List<DownloadThread> failList = new ArrayList<>(ConstantConfigs.threadNum);

    /**
     * 是否支持断点续传
     */
    public boolean isHeaders = false; // 是否支持断点续传

    /**
     * 是否未结束
     */
    public boolean isNotEnd = true; // 是否未结束


    /**
     * 是否停止
     */
    public boolean isNotStop = true; //


    /**
     * 下载进度
     */
    public int progressBar = 0; //下载进度

    /**
     * 文件格式
     */
    private String format = ""; // 文件格式

    /**
     * 下载中处理器 用于做任务广播
     */
    private Handler handler = new Handler();

    /**
     * 请求头信息
     */
    private Map<String, String> headers; // 请求头信息

    /**
     * 初始化 下载任务时设置的 网络请求监听器接口
     */
    IHttpRequestListener httplistener = new IHttpRequestListener() {
        @Override
        public void onFailure(Call call, IOException e) {
            Log.e(TAG, "onFailure 初始化下载任务失败");
            System.err.println("onFailure 初始化下载任务失败");
            if (listener != null) {
                onFail(call, e);
            }
        }

        @Override
        public void onResponse(Call call, Response response) {
            if (response.isSuccessful()) {
                String contentRange = response.header("Content-Range");
                String contentDisposition = response.header("Content-Disposition");

                format = contentDisposition.substring(contentDisposition.indexOf("."), contentDisposition.length());
                if (isNotNull(contentRange)) {
                    setIsHeaders(true);
                    // 启用断点续传分段下载
                    Log.i(TAG, "启用断点续传分段下载");
                    setDownloadSize(Integer.parseInt(contentRange.substring(contentRange.indexOf("/") + 1, contentRange.length())));
                } else {
                    setIsHeaders(false);
                    // 接口不支持断点续传和分段下载
                    Log.i(TAG, "接口不支持断点续传和分段下载");
                    setDownloadSize(Integer.parseInt(response.header("Content-Length")));
                }
                assignTasks();
            }
        }

        @Override
        public void onError(Exception e) {
            Log.e(TAG, "onError 初始化下载任务失败");
            System.err.println("onError 初始化下载任务失败");
            onFail(null, e);

        }
    };

    /**
     * 空参数构造器一般用于 build()方式的构建   建议不要修改 权限标识
     */
    private DownloadTasker() {
        super();
    }


    /**
     * 创建并且启动
     */
    public DownloadTasker create() {  // targetFile = new File(dirFile + File.separator + fileName + format);
        if (isNull(dirFile)) {
            onFail(null, new RuntimeException("下载的文件储存目录为空"));
            return this;
        }
        if (isNull(fileName)) {
            onFail(null, new RuntimeException("下载文件名称为空"));
            return this;
        }
        if (isNull(url)) {
            onFail(null, new RuntimeException("下载链接为空"));
            return this;
        }
        dir = new File(dirFile);
        if (!dir.exists() || !dir.isDirectory()) {
            dir.mkdirs();
        }
        if (headers == null) {
            headers = new HashMap<>();
        }
        headers.put("Range", "bytes=0-0");
        DownloadEntity entity = new DownloadEntity(0, dirFile, fileName, url, 0L, 0L, 1L);
        DownloadThread.build().listener(httplistener).entity(entity).headers(headers).start();
        // handler.start();
        return this;
    }

    /**
     * 重启
     *
     * @return
     */
    @Override
    public boolean restart() {
        try {
            Thread.sleep(ConstantConfigs.reTryInterval);
        } catch (InterruptedException interruptedException) {
            interruptedException.printStackTrace();
        }
        if (!isNotEnd && failList != null && failList.size() > 0) {
            isNotStop = true;
            for (DownloadThread entity : failList) {
                taskerFailRestart(entity);
            }
            failList.clear();
            handler = new Handler();
            // TODO
            handler.start();
            return true;
        }
        return false;
    }

    /**
     * 任务重启方法
     *
     * @param download
     */
    private void taskerFailRestart(DownloadThread download) {
        if (isHeaders) {
            DownloadEntity entity = new DownloadEntity(download.getEntity().serialNumber, download.getEntity().dirFile, download.getEntity().fileName, download.getEntity().url, download.getEntity().currentSize + 1, download.getEntity().getEndPosition(), download.getEntity().totalSize);
            Map<String, String> header = new HashMap<>();
            header.put("Range", "bytes=" + download.getEntity().getBeginPosition() + "-" + download.getEntity().getEndPosition());
            DownloadThread newThread = DownloadThread.build().entity(entity).headers(header).reTryNum(1);
            IHttpRequestListener listener = new IHttpRequestListener() {
                @Override
                public void onFailure(Call call, IOException e) {
                    onSubtaskFail(call, e, newThread);
                }

                @Override
                public void onResponse(Call call, Response response) {
                    newThread.response = response;
                    String contentRange = response.header("Content-Range");
                    newThread.getEntity().totalSize = Integer.parseInt(contentRange.substring(contentRange.indexOf("/") + 1, contentRange.length()));

                    InputStream inputStream = response.body().byteStream();
                    RandomAccessFile accessFile = null;
                    try {
                        File file = new File(newThread.getEntity().dirFile + File.separator + newThread.getEntity().fileName);
                        accessFile = new RandomAccessFile(file, "rw");
                        byte[] bytes = new byte[ConstantConfigs.diskWriteSpeed];
                        int len = 0;
                        while ((len = inputStream.read(bytes)) != -1) {
                            if (call.isCanceled()) {
                                Log.e(TAG, "下载任务已取消");
                                onSubtaskFail(call, new RuntimeException("下载任务已取消"), newThread);
                                return;
                            }
                            accessFile.write(bytes, 0, len);
                            newThread.getEntity().currentSize = newThread.getEntity().currentSize + len;
                        }
                        long size = file.length();
                        if (size > newThread.getEntity().totalSize) {
                            //  重载子任务
                            onSubtaskFail(call, new RuntimeException("下载后文件和任务长度不一样"), newThread);
                        } else {
                            Log.e(TAG, "第 [" + newThread.getEntity().serialNumber + "] 段下载结束【" + newThread.getEntity().fileName + "】完成下载");
                            newThread.isSuccess(WorkState.Success);
                            onSubtaskSuccess(newThread);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        newThread.isSuccess(WorkState.Fail);
                        onSubtaskFail(call, e, newThread);
                    } finally {
                        response.close();
                        try {
                            inputStream.close();
                            accessFile.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }

                @Override
                public void onError(Exception e) {
                    onSubtaskFail(null, e, newThread);
                }
            };
            newThread.listener(listener);
            subTasker.add(newThread);
            newThread.start();
        } else {
            String name = fileName + format;
            DownloadEntity entity = new DownloadEntity(0, dirFile, name, url, 0, getDownloadSize(), getDownloadSize());
            Map<String, String> header = new HashMap<>();
            DownloadThread newThread = DownloadThread.build().entity(entity).headers(header);
            IHttpRequestListener listener = new IHttpRequestListener() {
                @Override
                public void onFailure(Call call, IOException e) {
                    onSubtaskFail(call, e, newThread);
                }

                @Override
                public void onResponse(Call call, Response response) {
                    newThread.response = response;
                    newThread.getEntity().totalSize = Integer.parseInt(response.header("Content-Length"));

                    InputStream inputStream = response.body().byteStream();
                    RandomAccessFile accessFile = null;
                    try {
                        File file = new File(newThread.getEntity().dirFile + File.separator + newThread.getEntity().fileName);
                        accessFile = new RandomAccessFile(file, "rw");
                        byte[] bytes = new byte[ConstantConfigs.diskWriteSpeed];
                        int len = 0;
                        while ((len = inputStream.read(bytes)) != -1) {
                            if (call.isCanceled()) {
                                Log.e(TAG, "下载任务已取消");
                                onSubtaskFail(call, new RuntimeException("下载任务已取消"), newThread);
                                return;
                            }
                            accessFile.write(bytes, 0, len);
                            newThread.getEntity().currentSize = newThread.getEntity().currentSize + len;
                        }
                        long size = file.length();
                        if (size > newThread.getEntity().totalSize) {
                            //  重载子任务
                            onSubtaskFail(call, new RuntimeException("下载后文件和任务长度不一样"), newThread);
                        } else {
                            Log.e(TAG, "第 [" + newThread.getEntity().serialNumber + "] 段下载结束【" + newThread.getEntity().fileName + "】完成下载");
                            newThread.isSuccess(WorkState.Success);
                            onSubtaskSuccess(newThread);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        newThread.isSuccess(WorkState.Fail);
                        onSubtaskFail(call, e, newThread);
                    } finally {
                        response.close();
                        try {
                            inputStream.close();
                            accessFile.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }

                @Override
                public void onError(Exception e) {
                    onSubtaskFail(null, e, newThread);
                }
            };
            newThread.listener(listener);
            subTasker.add(newThread);
            newThread.start();
        }
    }

    /**
     * 暂停 TODO
     *
     * @param msec 停止毫秒数 0代表永久停止
     * @return
     */
    @Override
    public boolean onStop(long msec) {
        if (isNotStop) {
            isNotStop = false;//  结束 handler 运行中的处理器
            if (subTasker != null && subTasker.size() > 0) {
                List<DownloadThread> cloneTasker = new ArrayList<>();
                List<Response> responses = new ArrayList<>();
                for (DownloadThread entity : subTasker) {
                    if (entity.response != null) {
                        responses.add(entity.response);
                    }
                }
                if (responses.size() > 0) {
                    for (Response response : responses) {
                        try {
                            if (response != null) {
                                response.close();
                            }
                        } catch (Exception e) {

                        }
                    }
                }
                if (msec > 0L) {
                    try {
                        Thread.sleep(msec);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    onContinue();
                }
            }
            if (listener != null) {
                listener.onStop(msec);
            }
        }
        return true;
    }

    /**
     * 取消
     *
     * @return TODO
     */
    @Override
    public boolean onCancel() {
        isNotStop = false;//  结束 handler 运行中的处理器
        isNotEnd = false;
        if (subTasker != null && subTasker.size() > 0) {
            for (DownloadThread entity : subTasker) {
                try {
                    if (entity.response != null && entity.getIsSuccess() == WorkState.NotComplete) {
                        try {
                            entity.response.close();
                        } catch (Exception e) {

                        }
                    }
                } catch (Exception e) {

                }
                File file = new File(entity.getEntity().dirFile + File.separator + entity.getEntity().fileName);
                if (file.exists() && !file.isDirectory()) {
                    file.delete();
                }
            }
        }
        if (failList != null && failList.size() > 0) {
            for (DownloadThread download : failList) {
                try {
                    if (download.response != null && download.getIsSuccess() == WorkState.NotComplete) {
                        download.response.close();
                    }
                } catch (Exception e) {

                }
                File file = new File(download.getEntity().dirFile + File.separator + download.getEntity().fileName);
                if (file.exists() && !file.isDirectory()) {
                    file.delete();
                }
            }
        }
        if (listener != null) {
            listener.onCancel();
        }
        return true;
    }

    /**
     * 继续下载
     * 这里的继续下载只考虑启动正常子任务,失败任务应该在失败后考虑重启
     *
     * @return TODO
     */
    @Override
    public boolean onContinue() {
        if (!isNotStop) {
            isNotStop = true;
            reinitializationFailedTask(null, ReinitializationPattern.tentativeRestart);
        }
        return true;
    }

    private void newRunHandler() {
        if (handler != null) {
            handler = null;
        }
        handler = new Handler();
        // TODO
        handler.start();

        if (listener != null) {
            listener.onCancel();
        }
    }

    /**
     * 初始化下载文件信息
     */
    @Override
    public void onInit() {
        if (listener != null) {
            listener.onInit(this);
        }
    }

    /**
     * 开始下载
     */
    @Override
    public void onStart() {
        if (subTasker != null && subTasker.size() > 0) {
            for (DownloadThread thread : subTasker) {
                thread.start();
            }
        }
        handler.start();
        if (listener != null) {
            listener.onStart(this);
        }
    }

    /**
     * 下载失败
     *
     * @param call
     * @param e
     * @param
     */
    @Override
    public void onFail(Call call, Exception e) {
        isNotStop = false;//  结束 handler 运行中的处理器
        if (listener != null) {
            listener.onFail(call, e, this);
        }
    }

    /**
     * 子任务下载失败
     *
     * @param call
     * @param e
     * @param tasker
     */
    @Override
    public void onSubtaskFail(Call call, Exception e, DownloadThread tasker) {
        if (tasker.reTryNum < (ConstantConfigs.reTryNum + 1) && isNotEnd && isNotStop) {

            (new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(ConstantConfigs.reTryInterval);
                    } catch (InterruptedException interruptedException) {
                        interruptedException.printStackTrace();
                    }
                    reinitializationFailedTask(tasker, ReinitializationPattern.failedRestart);
                }
            })).start();
        } else {
            if ((tasker.reTryNum == ConstantConfigs.reTryNum || tasker.reTryNum > ConstantConfigs.reTryNum)) {
                // 最后一次下载失败的话删除下载文件
                File file = new File(tasker.getEntity().dirFile + File.separator + tasker.getEntity().fileName);
                if (file.exists() && !file.isDirectory()) {
                    file.delete();
                }
                tasker.isSuccess(WorkState.Fail);
                failList.add(tasker);
                subTasker.remove(tasker);
            } else {
                if (!isNotStop) {
                    tasker.isSuccess(WorkState.Fail);
                    failList.add(tasker);
                    subTasker.remove(tasker);
                }
            }
        }

        if (listener != null && !isNotStop) {
            listener.onSubtaskFail(call, e, tasker);
        }
    }

    /**
     * 子任务下载成功
     *
     * @param tasker
     */
    @Override
    public void onSubtaskSuccess(DownloadThread tasker) {
        if (!isHeaders) {
            onComplete(this);
        }
        if (listener != null) {
            listener.onSubtaskSuccess(tasker);
        }
    }

    /**
     * 下载完成
     *
     * @param tasker
     */
    @Override
    public void onComplete(Tasker tasker) {
        isNotEnd = false;//  结束 handler 运行中的处理器
        isNotStop = false;//  结束 handler 运行中的处理器

        if (isHeaders) {
            // ① 排序队列 文件顺序
            Collections.sort(subTasker, new Comparator<DownloadThread>() {
                @Override
                public int compare(DownloadThread frontThread, DownloadThread afterThread) {
                    int front = frontThread.getEntity().serialNumber;
                    int after = afterThread.getEntity().serialNumber;
                    return front - after;
                }
            });

            // ② 合并文件
            boolean isSuccess = mergeFile(dirFile + File.separator + fileName + format, subTasker);
            if (isSuccess) {
                for (DownloadThread thread : subTasker) {
                    File file = new File(thread.getEntity().dirFile + File.separator + thread.getEntity().fileName);
                    if (file.exists()) {
                        file.delete();
                    }
                }
            }
        }

        if (listener != null) {
            listener.onComplete(tasker);
        }
    }


    /**
     * 合并文件
     *
     * @param targetPath 目标文件
     * @param subTasker  碎片文件路径
     * @return {@code true} 合并成功，{@code false}合并失败
     */
    public boolean mergeFile(String targetPath, List<DownloadThread> subTasker) {
        Log.d(TAG, "开始合并文件");
        File targetFile = new File(targetPath);
        FileOutputStream fos = null;
        FileChannel foc = null;
        long startTime = System.currentTimeMillis();
        try {
            if (targetFile != null && targetFile.exists()) {
                Log.w(TAG, String.format("路径【%s】是文件夹，将删除该文件夹", targetPath));
                targetFile.delete();// 删除原来的文件
                targetFile.createNewFile();// 重新创建一个
            }

            fos = new FileOutputStream(targetPath);
            foc = fos.getChannel();
            List<FileInputStream> streams = new LinkedList<>();
            long fileLen = 0;
            for (DownloadThread thread : subTasker) {
                File f = new File(thread.getEntity().dirFile + File.separator + thread.getEntity().fileName);
                if (!f.exists()) {
                    Log.d(TAG, String.format("合并文件失败，文件【%s】不存在", thread.getEntity().dirFile + File.separator + thread.getEntity().fileName));
                    for (FileInputStream fis : streams) {
                        fis.close();
                    }
                    streams.clear();

                    return false;
                }
                FileInputStream fis = new FileInputStream(thread.getEntity().dirFile + File.separator + thread.getEntity().fileName);
                FileChannel fic = fis.getChannel();
                foc.transferFrom(fic, fileLen, f.length());
                fileLen += f.length();
                fis.close();

            }
            Log.d(TAG, String.format("合并文件耗时：%sms", (System.currentTimeMillis() - startTime)));
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (foc != null) {
                    foc.close();
                }
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }


    /**
     * 下载中 用于做任务广播
     * 多久启动一次 由 内部类 Handler 中的  Thread.sleep(ConstantConfigs.updateInterval) 这行代码决定
     */
    @Override
    public void onRunning() {
        listener.onRunning(this);
    }

    private class Handler extends Thread {
        @Override
        public void run() {
            super.run();
            while (isNotEnd && isNotStop) {
                if (listener != null) {
                    try {
                        Thread.sleep(ConstantConfigs.updateInterval);
                    } catch (InterruptedException e) {
                    }
                    handlerRunning();
                }
            }
        }
    }

    /**
     * 本函数用于
     * 处理下载进度，下载中
     */
    private void handlerRunning() {
        if (downloadSize != 0) { // 这里需要注意，除数不可为0
            // 计算下载进度
            this.progressBar = calculation(currentLocation, downloadSize); //  (int) length;
        }

        System.out.println("handlerRunning.... 当前任务【" + id + "】下载进度【" + progressBar + "】开始时间戳【" + taskId + "】纳秒");
        onRunning();
        if (subTasker != null && subTasker.size() > 0) {
            // 循环看看有没有下载未完成的任务
            for (DownloadThread ignored : subTasker) {
                //如果有 ，视为本次下次任务还在进行中，直接 return 出去
                if (ignored.getIsSuccess() == WorkState.NotComplete || ignored.getIsSuccess() == WorkState.Fail) {
                    return;
                }
            }
            boolean isHaveFail = false;// 是否有下载失败的线程
            for (DownloadThread ignored : subTasker) {
                if (ignored.getIsSuccess() == WorkState.Fail) {
                    System.out.println("第" + ignored.getEntity().getSerialNumber() + "子线程失败，引起全局下载失败");
                    onFail(null, new RuntimeException("第" + ignored.getEntity().getSerialNumber() + "子线程失败，引起全局下载失败"));
                    isNotEnd = false;
                    isHaveFail = true;
                }
            }
            if (!isHaveFail) {
                isNotEnd = false;
                onComplete(this);
            }
        } else {
            isNotStop = false;//  结束 handler 运行中的处理器
            System.out.println("handlerRunning.... 没有下载子任务了");
            onFail(null, new RuntimeException("没有下载子任务了"));
        }
    }

    /**
     * @param dividend 被除数
     * @param divisor  除数
     * @return
     */
    private int calculation(long dividend, long divisor) {
        BigDecimal head = new BigDecimal(dividend);
        BigDecimal tail = new BigDecimal(divisor);
        BigDecimal merchant = head.divide(tail, 2, BigDecimal.ROUND_HALF_DOWN); // 商
        BigDecimal product = merchant.multiply(new BigDecimal(100));// 积
        return product.intValue();
    }

    private boolean isNull(String content) {
        return content == null || content.trim().length() < 1;
    }

    private boolean isNotNull(String content) {
        return content != null && content.trim().length() > 0;
    }

    private Map<String, String> cloneHeaders(Map<String, String> headerMap) {
        Map<String, String> header = new HashMap<>();
        if (headerMap != null && !headerMap.isEmpty()) {
            Set<String> set = headerMap.keySet();
            for (String key : set) {
                String value = headerMap.get(key);
                header.put(key, value);
            }
        }
        return header;
    }

    private void assignTasks() {
        if (isHeaders && ConstantConfigs.useBlock && ConstantConfigs.threadNum > 1) {
            // 分配 分段下载任务
            /*DownloadEntity entity = new DownloadEntity(0, dirFile, fileName, url, 0, 0);
            DownloadThread.build().listener(httplistener).entity(entity).headers(headers).start();*/
            long mean = (long) downloadSize / ConstantConfigs.threadNum;// 每段平均下载长度
            long remainder = downloadSize - (ConstantConfigs.threadNum * mean); // 求出余数，看看 是不是有剩余的字节 如果有将剩余的字节加到最后一条线程里去
            for (int i = 0; i < ConstantConfigs.threadNum; i++) {
                int serialNumber = i;
                String name = fileName + "_" + i + "_" + format;
                long begin = i * mean;
                long toloat = mean;
                if (i == (ConstantConfigs.threadNum - 1) && remainder > 0L) {
                    toloat = mean + remainder; // 如果有将剩余的字节加到最后一条线程里去

                }
                long end = begin + toloat;
                DownloadEntity entity = new DownloadEntity(serialNumber, dirFile, name, url, begin, end, toloat);
                Map<String, String> header = cloneHeaders(headers);
                if (i == (ConstantConfigs.threadNum - 1)) {
                    header.put("Range", "bytes=" + begin + "-" + (downloadSize - 1));
                } else {
                    header.put("Range", "bytes=" + begin + "-" + end);
                }
                DownloadThread thread = DownloadThread.build().entity(entity).headers(header);
                IHttpRequestListener listener = new IHttpRequestListener() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                        onSubtaskFail(call, e, thread);
                    }

                    @Override
                    public void onResponse(Call call, Response response) {
                        thread.response = response;
                        String contentRange = response.header("Content-Range");
                        thread.getEntity().totalSize = Integer.parseInt(contentRange.substring(contentRange.indexOf("/") + 1, contentRange.length()));

                        InputStream inputStream = response.body().byteStream();
                        RandomAccessFile accessFile = null;
                        try {
                            File file = new File(thread.getEntity().dirFile + File.separator + thread.getEntity().fileName);
                            accessFile = new RandomAccessFile(file, "rw");
                            byte[] bytes = new byte[ConstantConfigs.diskWriteSpeed];
                            int len = 0;
                            while ((len = inputStream.read(bytes)) != -1) {
                                if (call.isCanceled()) {
                                    Log.e(TAG, "下载任务已取消");
                                    onSubtaskFail(call, new RuntimeException("下载任务已取消"), thread);
                                    return;
                                }
                                accessFile.write(bytes, 0, len);
                                thread.getEntity().currentSize = thread.getEntity().currentSize + len;
                                // thread.getEntity().setBeginPosition(thread.getEntity().getBeginPosition() + len);
                                increaseProgress(len); //  告诉主线程当前下次进度
                            }
                            long size = file.length();
                            if (size > thread.getEntity().totalSize) {
                                //  重载子任务
                                onSubtaskFail(call, new RuntimeException("下载后文件和任务长度不一样"), thread);
                            } else {
                                Log.e(TAG, "第 [" + thread.getEntity().serialNumber + "] 段下载结束【" + thread.getEntity().fileName + "】完成下载");
                                onSubtaskSuccess(thread);
                                thread.isSuccess(WorkState.Success);
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                            onSubtaskFail(call, e, thread);
                            thread.isSuccess(WorkState.Fail);
                        } finally {
                            response.close();
                            try {
                                inputStream.close();
                                accessFile.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }

                    @Override
                    public void onError(Exception e) {
                        onSubtaskFail(null, e, thread);
                    }
                };
                thread.listener(listener);
                subTasker.add(thread);

            }
        } else {
            String name = fileName + format;
            DownloadEntity entity = new DownloadEntity(0, dirFile, name, url, 0, getDownloadSize(), getDownloadSize());
            Map<String, String> header = cloneHeaders(headers);
            DownloadThread thread = DownloadThread.build().entity(entity).headers(header);
            IHttpRequestListener listener = new IHttpRequestListener() {
                @Override
                public void onFailure(Call call, IOException e) {
                    onSubtaskFail(call, e, thread);
                }

                @Override
                public void onResponse(Call call, Response response) {
                    thread.response = response;
                    thread.getEntity().totalSize = Integer.parseInt(response.header("Content-Length"));

                    InputStream inputStream = response.body().byteStream();
                    RandomAccessFile accessFile = null;
                    try {
                        File file = new File(thread.getEntity().dirFile + File.separator + thread.getEntity().fileName);
                        accessFile = new RandomAccessFile(file, "rw");
                        byte[] bytes = new byte[ConstantConfigs.diskWriteSpeed];
                        int len = 0;
                        while ((len = inputStream.read(bytes)) != -1) {
                            if (call.isCanceled()) {
                                Log.e(TAG, "下载任务已取消");
                                onSubtaskFail(call, new RuntimeException("下载任务已取消"), thread);
                                return;
                            }
                            accessFile.write(bytes, 0, len);
                            thread.getEntity().currentSize = thread.getEntity().currentSize + len;
                            // thread.getEntity().setBeginPosition(thread.getEntity().getBeginPosition() + len);
                            increaseProgress(len); //  告诉主线程当前下次进度
                        }
                        long size = file.length();
                        if (size > thread.getEntity().totalSize) {
                            //  重载子任务
                            onSubtaskFail(call, new RuntimeException("下载后文件和任务长度不一样"), thread);
                        } else {
                            Log.e(TAG, "第 [" + thread.getEntity().serialNumber + "] 段下载结束【" + thread.getEntity().fileName + "】完成下载");
                            onSubtaskSuccess(thread);
                            thread.isSuccess(WorkState.Success);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        onSubtaskFail(call, e, thread);
                        thread.isSuccess(WorkState.Fail);
                    } finally {
                        response.close();
                        try {
                            inputStream.close();
                            accessFile.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }

                @Override
                public void onError(Exception e) {
                    onSubtaskFail(null, e, thread);
                }
            };
            thread.listener(listener);
            subTasker.add(thread);
        }
        onStart();
    }

    /**
     * 子任务下载失败重新加载任务
     *
     * @param thread
     * @param thread 重新初始化模式
     */
    private synchronized void reinitializationFailedTask(DownloadThread thread, ReinitializationPattern pattern) {
        if (pattern == ReinitializationPattern.failedRestart) {
            DownloadThread oldThread = getSubDownloadThread(thread.getEntity().getChildrenTaskId());
            if (oldThread != null) {
                synchronized (oldThread) {
                    try {
                        Thread.sleep(500L);
                    } catch (InterruptedException interruptedException) {
                        interruptedException.printStackTrace();
                    }
                    oldThread = getSubDownloadThread(thread.getEntity().getChildrenTaskId());
                    if (oldThread != null) {
                        boolean remove = subTasker.remove(oldThread);
                        if (oldThread.reTryNum == thread.reTryNum && thread.getState() != Thread.State.NEW && thread.getState() != Thread.State.RUNNABLE) {
                            if (isHeaders) {
                                DownloadEntity entity = new DownloadEntity(thread.getEntity().serialNumber, thread.getEntity().dirFile, thread.getEntity().fileName, thread.getEntity().url, thread.getEntity().getBeginPosition(), thread.getEntity().getEndPosition(), thread.getEntity().totalSize);
                                Map<String, String> header = cloneHeaders(headers);
                                header.put("Range", "bytes=" + (entity.getBeginPosition() + entity.currentSize ) + "-" + thread.getEntity().getEndPosition());
                                DownloadThread newThread = DownloadThread.build().entity(entity).headers(header).reTryNum(oldThread.reTryNum + 1);
                                IHttpRequestListener listener = new IHttpRequestListener() {
                                    @Override
                                    public void onFailure(Call call, IOException e) {
                                        onSubtaskFail(call, e, newThread);
                                    }

                                    @Override
                                    public void onResponse(Call call, Response response) {
                                        newThread.response = response;
                                        String contentRange = response.header("Content-Range");
                                        newThread.getEntity().totalSize = Integer.parseInt(contentRange.substring(contentRange.indexOf("/") + 1, contentRange.length()));

                                        InputStream inputStream = response.body().byteStream();
                                        RandomAccessFile accessFile = null;
                                        try {
                                            File file = new File(newThread.getEntity().dirFile + File.separator + newThread.getEntity().fileName);
                                            accessFile = new RandomAccessFile(file, "rw");
                                            byte[] bytes = new byte[ConstantConfigs.diskWriteSpeed];
                                            int len = 0;
                                            while ((len = inputStream.read(bytes)) != -1) {
                                                if (call.isCanceled()) {
                                                    Log.e(TAG, "下载任务已取消");
                                                    onSubtaskFail(call, new RuntimeException("下载任务已取消"), newThread);
                                                    return;
                                                }
                                                accessFile.write(bytes, 0, len);
                                                newThread.getEntity().currentSize = newThread.getEntity().currentSize + len;
                                                // newThread.getEntity().setBeginPosition(newThread.getEntity().getBeginPosition() + len);
                                                increaseProgress(len); //  告诉主线程当前下次进度
                                            }
                                            long size = file.length();
                                            if (size > newThread.getEntity().totalSize) {
                                                //  重载子任务
                                                onSubtaskFail(call, new RuntimeException("下载后文件和任务长度不一样"), newThread);
                                            } else {
                                                Log.e(TAG, "第 [" + newThread.getEntity().serialNumber + "] 段下载结束【" + newThread.getEntity().fileName + "】完成下载");
                                                newThread.isSuccess(WorkState.Success);
                                                onSubtaskSuccess(newThread);
                                            }
                                        } catch (IOException e) {
                                            e.printStackTrace();
                                            newThread.isSuccess(WorkState.Fail);
                                            onSubtaskFail(call, e, newThread);
                                        } finally {
                                            response.close();
                                            try {
                                                inputStream.close();
                                                accessFile.close();
                                            } catch (IOException e) {
                                                e.printStackTrace();
                                            }
                                        }
                                    }

                                    @Override
                                    public void onError(Exception e) {
                                        onSubtaskFail(null, e, newThread);
                                    }
                                };
                                newThread.listener(listener);
                                subTasker.add(newThread);
                                if (thread.getEntity().getBeginPosition() == thread.getEntity().getEndPosition()) {
                                    newThread.setIsSuccess(WorkState.Success);
                                    onSubtaskSuccess(newThread);
                                } else {
                                    newThread.start();
                                }
                            } else {
                                String name = fileName + format;
                                DownloadEntity entity = new DownloadEntity(0, dirFile, name, url, 0, getDownloadSize(), getDownloadSize());
                                Map<String, String> header = new HashMap<>();
                                DownloadThread newThread = DownloadThread.build().entity(entity).headers(header);
                                IHttpRequestListener listener = new IHttpRequestListener() {
                                    @Override
                                    public void onFailure(Call call, IOException e) {
                                        onSubtaskFail(call, e, newThread);
                                    }

                                    @Override
                                    public void onResponse(Call call, Response response) {
                                        newThread.response = response;
                                        newThread.getEntity().totalSize = Integer.parseInt(response.header("Content-Length"));

                                        InputStream inputStream = response.body().byteStream();
                                        RandomAccessFile accessFile = null;
                                        try {
                                            File file = new File(newThread.getEntity().dirFile + File.separator + newThread.getEntity().fileName);
                                            accessFile = new RandomAccessFile(file, "rw");
                                            byte[] bytes = new byte[ConstantConfigs.diskWriteSpeed];
                                            int len = 0;
                                            while ((len = inputStream.read(bytes)) != -1) {
                                                if (call.isCanceled()) {
                                                    Log.e(TAG, "下载任务已取消");
                                                    onSubtaskFail(call, new RuntimeException("下载任务已取消"), newThread);
                                                    return;
                                                }
                                                accessFile.write(bytes, 0, len);
                                                newThread.getEntity().currentSize = newThread.getEntity().currentSize + len;
                                                // newThread.getEntity().setBeginPosition(newThread.getEntity().getBeginPosition() + len);
                                                increaseProgress(len); //  告诉主线程当前下次进度
                                            }
                                            long size = file.length();
                                            if (size > newThread.getEntity().totalSize) {
                                                //  重载子任务
                                                onSubtaskFail(call, new RuntimeException("下载后文件和任务长度不一样"), newThread);
                                            } else {
                                                Log.e(TAG, "第 [" + newThread.getEntity().serialNumber + "] 段下载结束【" + newThread.getEntity().fileName + "】完成下载");
                                                newThread.isSuccess(WorkState.Success);
                                                onSubtaskSuccess(newThread);
                                            }
                                        } catch (IOException e) {
                                            e.printStackTrace();
                                            newThread.isSuccess(WorkState.Fail);
                                            onSubtaskFail(call, e, newThread);
                                        } finally {
                                            response.close();
                                            try {
                                                inputStream.close();
                                                accessFile.close();
                                            } catch (IOException e) {
                                                e.printStackTrace();
                                            }
                                        }
                                    }

                                    @Override
                                    public void onError(Exception e) {
                                        onSubtaskFail(null, e, newThread);
                                    }
                                };
                                newThread.listener(listener);
                                subTasker.add(newThread);
                                newThread.start();
                            }
                        }
                    }
                }
            }
        } else if (pattern == ReinitializationPattern.tentativeRestart) {
            if (failList != null && failList.size() > 0) {
                synchronized (failList) {
                    List<DownloadThread> taskers = new ArrayList<>();
                    // 先复制所有子任务
                    for (DownloadThread tasker : failList) {
                        DownloadThread newTaker = DownloadThread.build()
                                .entity(tasker.getEntity())
                                .reTryNum(tasker.reTryNum)
                                .isSuccess(tasker.getIsSuccess());
                        newTaker.setIsSuccess(WorkState.NotComplete);
                        IHttpRequestListener requestListener = getHttpRequestListener(newTaker);
                        newTaker.listener(requestListener);
                        if (newTaker.getEntity().getBeginPosition() == newTaker.getEntity().getEndPosition()) {
                            onSubtaskSuccess(newTaker);
                            newTaker.setIsSuccess(WorkState.Success);
                            taskers.add(newTaker);
                        } else {
                            newTaker.start();
                            taskers.add(newTaker);
                        }
                    }
                    subTasker = taskers;
                }
            }
            newRunHandler();
        }

    }

    public IHttpRequestListener getHttpRequestListener(DownloadThread newThread) {
        IHttpRequestListener requestListener = null;
        Map<String, String> header = cloneHeaders(headers);
        if (isHeaders) {
            requestListener = new IHttpRequestListener() {
                @Override
                public void onFailure(Call call, IOException e) {
                    onSubtaskFail(call, e, newThread);
                }

                @Override
                public void onResponse(Call call, Response response) {
                    newThread.response = response;
                    String contentRange = response.header("Content-Range");
                    newThread.getEntity().totalSize = Integer.parseInt(contentRange.substring(contentRange.indexOf("/") + 1, contentRange.length()));

                    InputStream inputStream = response.body().byteStream();
                    RandomAccessFile accessFile = null;
                    try {
                        File file = new File(newThread.getEntity().dirFile + File.separator + newThread.getEntity().fileName);
                        accessFile = new RandomAccessFile(file, "rw");
                        byte[] bytes = new byte[ConstantConfigs.diskWriteSpeed];
                        int len = 0;
                        if (ConstantConfigs.threadNum > 1 && newThread.getEntity().getSerialNumber() == (ConstantConfigs.threadNum - 1)) {
                            System.out.println(newThread);
                        }
                        while ((len = inputStream.read(bytes)) != -1) {
                            if (call.isCanceled()) {
                                Log.e(TAG, "下载任务已取消");
                                onSubtaskFail(call, new RuntimeException("下载任务已取消"), newThread);
                                return;
                            }
                            accessFile.write(bytes, 0, len);
                            newThread.getEntity().currentSize = newThread.getEntity().currentSize + len;
                            newThread.getEntity().setBeginPosition(newThread.getEntity().getBeginPosition() + len);
                            increaseProgress(len); //  告诉主线程当前下次进度
                        }
                        long size = file.length();
                        if (size > newThread.getEntity().totalSize) {
                            //  重载子任务
                            onSubtaskFail(call, new RuntimeException("下载后文件和任务长度不一样"), newThread);
                        } else {
                            Log.e(TAG, "第 [" + newThread.getEntity().serialNumber + "] 段下载结束【" + newThread.getEntity().fileName + "】完成下载");
                            newThread.isSuccess(WorkState.Success);
                            onSubtaskSuccess(newThread);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        newThread.isSuccess(WorkState.Fail);
                        onSubtaskFail(call, e, newThread);
                    } finally {
                        response.close();
                        try {
                            inputStream.close();
                            accessFile.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }

                @Override
                public void onError(Exception e) {
                    onSubtaskFail(null, e, newThread);
                }
            };
            header.put("Range", "bytes=" + newThread.getEntity().getBeginPosition() + "-" + newThread.getEntity().getEndPosition());

        } else {
            requestListener = new IHttpRequestListener() {
                @Override
                public void onFailure(Call call, IOException e) {
                    onSubtaskFail(call, e, newThread);
                }

                @Override
                public void onResponse(Call call, Response response) {
                    newThread.response = response;
                    newThread.getEntity().totalSize = Integer.parseInt(response.header("Content-Length"));

                    InputStream inputStream = response.body().byteStream();
                    RandomAccessFile accessFile = null;
                    try {
                        File file = new File(newThread.getEntity().dirFile + File.separator + newThread.getEntity().fileName);
                        accessFile = new RandomAccessFile(file, "rw");
                        byte[] bytes = new byte[ConstantConfigs.diskWriteSpeed];
                        int len = 0;
                        while ((len = inputStream.read(bytes)) != -1) {
                            if (call.isCanceled()) {
                                Log.e(TAG, "下载任务已取消");
                                onSubtaskFail(call, new RuntimeException("下载任务已取消"), newThread);
                                return;
                            }
                            accessFile.write(bytes, 0, len);
                            newThread.getEntity().currentSize = newThread.getEntity().currentSize + len;
                            newThread.getEntity().setBeginPosition(newThread.getEntity().getBeginPosition() + len);
                            increaseProgress(len); //  告诉主线程当前下次进度
                        }
                        long size = file.length();
                        if (size > newThread.getEntity().totalSize) {
                            //  重载子任务
                            onSubtaskFail(call, new RuntimeException("下载后文件和任务长度不一样"), newThread);
                        } else {
                            Log.e(TAG, "第 [" + newThread.getEntity().serialNumber + "] 段下载结束【" + newThread.getEntity().fileName + "】完成下载");
                            newThread.isSuccess(WorkState.Success);
                            onSubtaskSuccess(newThread);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        newThread.isSuccess(WorkState.Fail);
                        onSubtaskFail(call, e, newThread);
                    } finally {
                        response.close();
                        try {
                            inputStream.close();
                            accessFile.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }

                @Override
                public void onError(Exception e) {
                    onSubtaskFail(null, e, newThread);
                }
            };
        }
        newThread.headers(header);
        return requestListener;
    }

    private DownloadThread getSubDownloadThread(long id) {
        DownloadThread downloadThread = null;
        if (subTasker != null && subTasker.size() > 0) {
            for (DownloadThread thread : subTasker) {
                if (thread.getEntity().getChildrenTaskId() == id) {
                    downloadThread = thread;
                    break;
                }
            }
        }
        return downloadThread;
    }


    public static DownloadTasker build() {
        return new DownloadTasker();
    }

    public DownloadTasker dirFile(String dirFile) {
        setDirFile(dirFile);
        return this;
    }

    public DownloadTasker fileName(String fileName) {
        setFileName(fileName);
        return this;
    }

    public DownloadTasker url(String url) {
        setUrl(url);
        return this;
    }

    public DownloadTasker listener(IDownloadListener listener) {
        setListener(listener);
        return this;
    }

    public DownloadTasker headers(Map<String, String> headers) {
        setHeaders(headers);
        return this;
    }

    public boolean getIsHeaders() {
        return isHeaders;
    }

    private void setIsHeaders(boolean headers) {
        isHeaders = headers;
    }

    public String getDirFile() {
        return dirFile;
    }

    private void setDirFile(String dirFile) {
        this.dirFile = dirFile;
    }

    public String getFileName() {
        return fileName;
    }

    private void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public String getUrl() {
        return url;
    }

    private void setUrl(String url) {
        this.url = url;
    }

    public long getDownloadSize() {
        return downloadSize;
    }

    private void setDownloadSize(long downloadSize) {
        this.downloadSize = downloadSize;
    }

    private void setListener(IDownloadListener listener) {
        this.listener = listener;
    }

    private void setHeaders(Map<String, String> headers) {
        this.headers = headers;
    }

    /**
     * 总监当前下载数量
     *
     * @param length
     */
    private void increaseProgress(int length) {
        this.currentLocation = this.currentLocation + length;
    }

}
